Replication of Fig 2 of Christenson et al 2010
dta_Mx %>%
filter(sex != "total") %>%
filter(age %in% c(80, 90)) %>%
filter(
code %in% c("GBRTENW", "FRATNP", "DEUTE", "DEUTW", "JPN", "SWE", "USA")
) %>%
filter(between(year, 1950, 2003)) %>%
ggplot(
aes(x = year, y = Mx, colour = code, group = code)
) +
geom_line() +
facet_grid(age ~ sex)

And how has this developed since?
dta_Mx %>%
filter(sex != "total") %>%
filter(age %in% c(80, 90)) %>%
filter(
code %in% c("GBRTENW", "FRATNP", "DEUTE", "DEUTW", "JPN", "SWE", "USA")
) %>%
filter(between(year, 1950, 2017)) %>%
ggplot(
aes(x = year, y = Mx, colour = code, group = code)
) +
geom_line() +
facet_grid(age ~ sex)

It’s interesting that life expectancies have at age 80 have converged, for males especially. And that they’ve levelled off in Japan over the 2000s, while continued to improve in the USA.
In England/Wales they Mx at age 90 seem to have increased for females especially in recent years.
Let’s look at this for the average of the 21 countries
Now let’s do this for the average of the 21 high income countries
dta_Mx %>%
filter(sex != "total") %>%
filter(age %in% c(80, 90)) %>%
filter(code %in% high_income_countries) %>%
filter(between(year, 1950, 2016)) %>%
group_by(year, age, sex) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
ggplot(aes(x = year, y = mean_Mx, colour = sex)) +
facet_wrap(~age) +
geom_point()

Let’s now use log rather than linear
dta_Mx %>%
filter(sex != "total") %>%
filter(age %in% c(80, 90)) %>%
filter(code %in% high_income_countries) %>%
filter(between(year, 1950, 2016)) %>%
group_by(year, age, sex) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
ggplot(aes(x = year, y = mean_Mx, colour = sex)) +
facet_wrap(~age) +
geom_point() +
scale_y_log10()

So, the trends on % reductions have been more continuous for age 90 than age 80, and more continuous for females than males.
Let’s now do this for a couple of additional age groups
dta_Mx %>%
filter(sex != "total") %>%
filter(age %in% c(0, 30, 80, 90)) %>%
filter(code %in% high_income_countries) %>%
filter(between(year, 1950, 2016)) %>%
group_by(year, age, sex) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
ggplot(aes(x = year, y = mean_Mx, colour = sex)) +
facet_wrap(~age) +
geom_point() +
scale_y_log10()

Now let’s estimate the correlation between these trends
dta_trnd <- dta_Mx %>%
filter(sex == "total") %>%
filter(between(year, 1955, 2016)) %>%
filter(age %in% c(0, 30, 80, 90)) %>%
group_by(year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx, 10))
dta_trnd %>%
select(-mean_Mx) %>%
mutate(age = paste0("age_", age)) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
age_0 age_30 age_80 age_90
age_0 1.0000000 0.9201582 0.9765313 0.9827364
age_30 0.9201582 1.0000000 0.9502384 0.9410485
age_80 0.9765313 0.9502384 1.0000000 0.9855633
age_90 0.9827364 0.9410485 0.9855633 1.0000000
So, as expected, % improvements in infancy are more strongly correlated with those at age 80 and 90 than at age 30, and % changes at age 90 are less strongly correlated than with those at other ages.
Let’s do this as a heatmap for all ages
dta_trnd <- dta_Mx %>%
filter(sex == "total") %>%
filter(between(year, 1955, 2016)) %>%
filter(age <= 109) %>%
group_by(year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx, 10))
tmp <- dta_trnd %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
cor_df <- tmp %>%
as_tibble() %>%
mutate(from_age = rownames(tmp)) %>%
gather(key="to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
cor_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c() +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal()

Definitely a ‘wow’ figure!
Interesting that the trends that apply at older ages don’t apply at the oldest ages (from around age 96 onwards)
Let’s see how different it looks by gender.
dta_trnd <- dta_Mx %>%
filter(sex != "total") %>%
filter(between(year, 1955, 2016)) %>%
filter(age <= 109) %>%
group_by(sex, year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx, 10)) %>%
group_by(sex) %>%
nest()
cors_df <- dta_trnd %>%
mutate(cors = map(
data,
function(X) {
X %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
}
)
) %>%
mutate(cor_df = map(
cors,
function(X){
X %>%
as_tibble() %>%
mutate(from_age = rownames(X)) %>%
gather(key = "to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
}
)
) %>%
select(sex, cor_df) %>%
unnest()
cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c() +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal() +
facet_wrap(~sex)

Again, beautiful, staggering, and awesome. This is like seeing the genome of population health improvement being mapped. The gender differences in the differences in correlation are very apparent.
Let’s extend this to a few few select countries:
- France
- Sweden?
- UK
- USA
- Spain (Identified as most compatible with Lee-Carter modelling approach)
- Japan
dta_trnd <- dta_Mx %>%
filter(sex != "total") %>%
filter(code %in% c("FRATNP", "SWE", "GBR_NP", "USA", "ESP", "JPN")) %>%
filter(between(year, 1955, 2016)) %>%
filter(age <= 109) %>%
group_by(code, sex, year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx + 0.00001, 10)) %>% # Correction for Sweden
group_by(sex, code) %>%
nest()
cors_df <- dta_trnd %>%
mutate(cors = map(
data,
function(X) {
X %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
}
)
) %>%
mutate(cor_df = map(
cors,
function(X){
X %>%
as_tibble() %>%
mutate(from_age = rownames(X)) %>%
gather(key = "to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
}
)
) %>%
select(sex, code, cor_df) %>%
unnest()
cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c(limits = c(-1,1)) +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal() +
facet_grid(sex ~ code)

Let’s do this just for the UK nations
dta_trnd <- dta_Mx %>%
filter(sex != "total") %>%
filter(code %in% c("GBRTENW", "GBR_SCO", "GBR_NIR")) %>%
filter(between(year, 1955, 2016)) %>%
filter(age <= 109) %>%
group_by(code, sex, year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx + 0.00001, 10)) %>% # Correction for Sweden
group_by(sex, code) %>%
nest()
cors_df <- dta_trnd %>%
mutate(cors = map(
data,
function(X) {
X %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
}
)
) %>%
mutate(cor_df = map(
cors,
function(X){
X %>%
as_tibble() %>%
mutate(from_age = rownames(X)) %>%
gather(key = "to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
}
)
) %>%
select(sex, code, cor_df) %>%
unnest()
cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c(limits = c(-1,1)) +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal() +
facet_grid(sex ~code) +
labs(x = "Age", y = "Age", title = "Correlations between rates of change in log mortality risk age different ages",
subtitle = "Trends from 1955-2016. England & Wales, Scotland, Northern Ireland",
caption = "Source: Human Mortality Database. Available from https://github.com/JonMinton/Life_expectancy_limits/")
ggsave("figures/trend_correlation_heatmap.png", height = 20, width = 25, units = "cm", dpi = 300)

Although plotly doesn’t convert faceting too easily, can I use ggplotly for hovering over values for Scotland males alone?
p <- cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
filter(sex == "male") %>%
filter(code == "GBR_SCO") %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c() +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal()
p <- cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c() +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal() +
facet_grid(sex ~code)
plotly::ggplotly(p)
ggplotly now works with faceted plots! (Though the number of observations looks like it causes things to struggle!)
For Scotland and England & Wales (independently), how have the correlations changed over time?
England/Wales first:
dta_trnd <- dta_Mx %>%
filter(sex != "total") %>%
filter(code %in% c("GBRTENW")) %>%
filter(between(year, 1950, 2010)) %>%
filter(age <= 109) %>%
mutate(
decade = cut(year, breaks = seq(1950, 2010, by = 10), labels = c("1950s", "1960s", "1970s", "1980s", "1990s", "2000s"), include.lowest = TRUE)
) %>%
group_by(sex, decade, year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx, 10)) %>% # Correction for Sweden
group_by(sex, decade) %>%
nest()
cors_df <- dta_trnd %>%
mutate(cors = map(
data,
function(X) {
X %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
}
)
) %>%
mutate(cor_df = map(
cors,
function(X){
X %>%
as_tibble() %>%
mutate(from_age = rownames(X)) %>%
gather(key = "to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
}
)
) %>%
select(sex, decade, cor_df) %>%
unnest()
cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c() +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal() +
facet_grid(sex ~ decade)

Now for Scotland.
dta_trnd <- dta_Mx %>%
filter(sex != "total") %>%
filter(code %in% c("GBR_SCO")) %>%
filter(between(year, 1950, 2010)) %>%
filter(age <= 109) %>%
mutate(
decade = cut(year, breaks = seq(1950, 2010, by = 10), labels = c("1950s", "1960s", "1970s", "1980s", "1990s", "2000s"), include.lowest = TRUE)
) %>%
group_by(sex, decade, year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx + 0.00001, 10)) %>% # Correction for Sweden
group_by(sex, decade) %>%
nest()
cors_df <- dta_trnd %>%
mutate(cors = map(
data,
function(X) {
X %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
}
)
) %>%
mutate(cor_df = map(
cors,
function(X){
X %>%
as_tibble() %>%
mutate(from_age = rownames(X)) %>%
gather(key = "to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
}
)
) %>%
select(sex, decade, cor_df) %>%
unnest()
cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c() +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal() +
facet_grid(sex ~ decade)

Northern Ireland
dta_trnd <- dta_Mx %>%
filter(sex != "total") %>%
filter(code %in% c("GBR_NIR")) %>%
filter(between(year, 1950, 2010)) %>%
filter(age <= 109) %>%
mutate(
decade = cut(year, breaks = seq(1950, 2010, by = 10), labels = c("1950s", "1960s", "1970s", "1980s", "1990s", "2000s"), include.lowest = TRUE)
) %>%
group_by(sex, decade, year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx + 0.00001, 10)) %>% # Correction for Sweden
group_by(sex, decade) %>%
nest()
cors_df <- dta_trnd %>%
mutate(cors = map(
data,
function(X) {
X %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
}
)
) %>%
mutate(cor_df = map(
cors,
function(X){
X %>%
as_tibble() %>%
mutate(from_age = rownames(X)) %>%
gather(key = "to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
}
)
) %>%
select(sex, decade, cor_df) %>%
unnest()
cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c() +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal() +
facet_grid(sex ~ decade)

dta_trnd <- dta_Mx %>%
filter(sex != "total") %>%
filter(code %in% c("GBRTENW")) %>%
filter(between(year, 1955, 2015)) %>%
filter(age <= 109) %>%
mutate(
decade = cut(year, breaks = seq(1955, 2015, by = 10), include.lowest = TRUE)
) %>%
group_by(sex, decade, year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx, 10)) %>% # Correction for Sweden
group_by(sex, decade) %>%
nest()
cors_df <- dta_trnd %>%
mutate(cors = map(
data,
function(X) {
X %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
}
)
) %>%
mutate(cor_df = map(
cors,
function(X){
X %>%
as_tibble() %>%
mutate(from_age = rownames(X)) %>%
gather(key = "to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
}
)
) %>%
select(sex, decade, cor_df) %>%
unnest()
cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c() +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal() +
facet_grid(sex ~ decade) +
labs(
x = "Age", y = "Age",
title = "Correlation between trends at indivdual ages, England & Wales, by decade"
)

Now for Scotland.
dta_trnd <- dta_Mx %>%
filter(sex != "total") %>%
filter(code %in% c("GBR_SCO")) %>%
filter(between(year, 1955, 2015)) %>%
filter(age <= 109) %>%
mutate(
decade = cut(year, breaks = seq(1955, 2015, by = 10), include.lowest = TRUE)
) %>%
group_by(sex, decade, year, age) %>%
summarise(mean_Mx = mean(Mx, na.rm = T)) %>%
ungroup() %>%
mutate(log_mean_Mx = log(mean_Mx + 0.00001, 10)) %>% # Correction for Sweden
group_by(sex, decade) %>%
nest()
cors_df <- dta_trnd %>%
mutate(cors = map(
data,
function(X) {
X %>%
select(-mean_Mx) %>%
spread(age, log_mean_Mx) %>%
select(-year) %>%
cor()
}
)
) %>%
mutate(cor_df = map(
cors,
function(X){
X %>%
as_tibble() %>%
mutate(from_age = rownames(X)) %>%
gather(key = "to_age", value = "value", -from_age) %>%
mutate(from_age = as.numeric(from_age), to_age = as.numeric(to_age))
}
)
) %>%
select(sex, decade, cor_df) %>%
unnest()
cors_df %>%
filter(from_age <= 100, to_age <= 100) %>%
ggplot(aes(x = from_age, y = to_age, fill = value)) +
geom_tile() +
scale_fill_viridis_c() +
scale_x_continuous(breaks = seq(0, 100, by = 10)) +
scale_y_continuous(breaks = seq(0, 100, by = 10)) +
coord_equal() +
facet_grid(sex ~ decade)

Analysis/results for aqmen conference presentation
Populations:
Time period:
- 1970 to last available year
Analyses:
- Individual plots
- Correlations between broad age groups
- Dendrogram of age correlations
dta_Mx %>%
filter(code %in% c("FRATNP", "GBR_NP", "USA", "JPN")) %>%
filter(year >= 1970) %>%
filter(age <= 109) %>%
filter(sex != "total") %>%
group_by(code, sex) %>%
nest()
Let’s look at hclust examples
It seems from this that the datastructure required should have the following:
- rownames: the individual ages
- columns: rate in different years
Let’s start with a single population: USA males
dta_Mx %>%
filter(code == "USA") %>%
filter(sex == "male") %>%
filter(year >= 1970) %>%
filter(age <= 109) %>%
mutate(lnMx = log(Mx + 0.00001, 10)) %>%
select(-code, -sex, -Mx) %>%
spread(year, lnMx) -> tmp
nms <- tmp$age
tmp$age <- NULL
rownames(tmp) <- nms
Setting row names on a tibble is deprecated.
tmp
hc <- hclust(dist(tmp), "ave")
ggdendro::ggdendrogram(hc, rotate = TRUE, leaf_labels = TRUE) + coord_flip()
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

This has been a bit too much of a drain on my cognitive resources for now. I’m going to stop and reevaluate when I’ve got more focus. It does seem to identify two broad clusters, and for these clusters to be as expected, split between working age and retiremnt age, with age 0 with post retirement age.
Let’s quickly try PCA on the same
pc <- prcomp(tmp, scale = TRUE, center = TRUE)
pc_df <- tibble(age = rownames(pc$x), pc1 = pc$x[,1], pc2 = pc$x[,2], pc3 = pc$x[,3])
ggplot(pc_df, aes(x = pc1, y = pc2, label = age)) +
geom_text()

ggplot(pc_df, aes(x = pc2, y = pc3, label = age)) +
geom_text()



LS0tDQp0aXRsZTogIkFnZSBTcGVjaWZpYyBNb3J0YWxpdHkgUmF0ZSBUcmVuZHMgIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyBJbnRybw0KDQpUaGlzIGRvYyB3aWxsIGRlc2NyaWJlIHNvbWUgb2YgdGhlIGF2ZXJhZ2UgdHJlbmRzIGF0IHNwZWNpZmljIGFnZXMgZnJvbSAyMSBoaWdoIGluY29tZSBjb3VudHJpZXMuDQoNCkl0IGlzIGluc3BpcmVkIGJ5IFtDaHJpc3RlbnNlbiAyMDEwXShodHRwczovL3d3dy50aGVsYW5jZXQuY29tL2pvdXJuYWxzL2xhbmNldC9hcnRpY2xlL1BJSVMwMTQwLTY3MzYoMDkpNjE0NjAtNC9mdWxsdGV4dCksIHdoaWNoIHNob3dlZA0KDQoqIFByb2JhYmlsaXR5IG9mIGR5aW5nIGluIG5leHQgMTIgbW9udGhzDQoqIEF0IGFnZSA4MCBhbmQgYWdlIDkwDQoqIE1hbGVzIGFuZCBmZW1hbGVzIA0KKiBTZWxlY3RlZCBjb3VudHJpZXMgDQogICAgKiBFbmdsYW5kICYgV2FsZXMNCiAgICAqIEZyYW5jZQ0KICAgICogRWFzdCBHZXJtYW55DQogICAgKiBXZXN0IEdlcm1hbnkNCiAgICAqIEphcGFuDQogICAgKiBTd2VkZW4NCiAgICAqIFVTQQ0KICANCiAgDQpIb3dldmVyLCBhcyBwZXIgdGhlIGZpZ3VyZXMgaW4gW1doaXRlIDIwMDJdKGh0dHBzOi8vb25saW5lbGlicmFyeS53aWxleS5jb20vZG9pL3BkZi8xMC4xMTExL2ouMTcyOC00NDU3LjIwMDIuMDAwNTkueCkpLCB0aGUgYXZlcmFnZSBvZiAyMSBoaWdoIGluY29tZSBjb3VudHJpZXMgd2lsbCBiZSB1c2VkIGluc3RlYWQsIGFzIHdlbGwgYXMgdGhlIGJlc3QtcGVyZm9ybWluZyBjb3VudHJ5IGF0IHRoZXNlIGRpZmZlcmVudCBhZ2UgZ3JvdXBzLiANCg0KQWRkaXRpb25hbGx5LCB0aGUgMTIgbW9udGggbW9ydGFsaXR5IHByb2JhYmlsaXRpZXMgYXQgdGhlIGZvbGxvd2luZyBhZ2VzIHdpbGwgYWxzbyBiZSBjYWxjdWxhdGVkOg0KDQoqIDAtMSB5ZWFycw0KKiA0MCB5ZWFycw0KDQpUaGUgYWltIHdpbGwgYmUgdG8gZGV0ZXJtaW5lIGhvdyBzdHJvbmdseSB0aGUgKGxvZyBvZiB0aGVzZSkgdHJlbmRzIGFyZSBjb3JyZWxhdGVkLiANCg0KIyBQcmUgcmVxcw0KDQoNCmBgYHtyfQ0KcGFjbWFuOjpwX2xvYWQoDQogIHRpZHl2ZXJzZSwgSE1ESEZEcGx1cywNCiAgZ2dyZXBlbCwgcGxvdGx5DQopDQoNCmR0YV9NeCA8LSByZWFkX3JkcygidGlkeV9kYXRhL014X2RhdGEucmRzIikNCg0KYGBgDQoNCiBEZWZpbmUgY291bnRyaWVzIA0KYGBge3J9DQpzb3VyY2UoInNjcmlwdHMvY291bnRyeV9kZWZpbml0aW9ucy5SIikNCg0KDQpgYGANCg0KIyBSZXBsaWNhdGlvbiBvZiBGaWcgMiBvZiBDaHJpc3RlbnNvbiBldCBhbCAyMDEwDQoNCmBgYHtyfQ0KDQpkdGFfTXggJT4lIA0KICBmaWx0ZXIoc2V4ICE9ICJ0b3RhbCIpICU+JSANCiAgZmlsdGVyKGFnZSAlaW4lIGMoODAsIDkwKSkgJT4lIA0KICBmaWx0ZXIoDQogICAgY29kZSAlaW4lIGMoIkdCUlRFTlciLCAiRlJBVE5QIiwgIkRFVVRFIiwgIkRFVVRXIiwgIkpQTiIsICJTV0UiLCAiVVNBIikNCiAgKSAlPiUgICAgICANCiAgZmlsdGVyKGJldHdlZW4oeWVhciwgMTk1MCwgMjAwMykpICU+JSANCiAgZ2dwbG90KA0KICAgIGFlcyh4ID0geWVhciwgeSA9IE14LCBjb2xvdXIgPSBjb2RlLCBncm91cCA9IGNvZGUpDQogICkgKyANCiAgZ2VvbV9saW5lKCkgKw0KICBmYWNldF9ncmlkKGFnZSB+IHNleCkNCg0KYGBgDQoNCkFuZCBob3cgaGFzIHRoaXMgZGV2ZWxvcGVkIHNpbmNlPw0KDQpgYGB7cn0NCg0KZHRhX014ICU+JSANCiAgZmlsdGVyKHNleCAhPSAidG90YWwiKSAlPiUgDQogIGZpbHRlcihhZ2UgJWluJSBjKDgwLCA5MCkpICU+JSANCiAgZmlsdGVyKA0KICAgIGNvZGUgJWluJSBjKCJHQlJURU5XIiwgIkZSQVROUCIsICJERVVURSIsICJERVVUVyIsICJKUE4iLCAiU1dFIiwgIlVTQSIpDQogICkgJT4lICAgICAgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTAsIDIwMTcpKSAlPiUgDQogIGdncGxvdCgNCiAgICBhZXMoeCA9IHllYXIsIHkgPSBNeCwgY29sb3VyID0gY29kZSwgZ3JvdXAgPSBjb2RlKQ0KICApICsgDQogIGdlb21fbGluZSgpICsNCiAgZmFjZXRfZ3JpZChhZ2UgfiBzZXgpDQoNCmBgYA0KDQpJdCdzIGludGVyZXN0aW5nIHRoYXQgbGlmZSBleHBlY3RhbmNpZXMgaGF2ZSBhdCBhZ2UgODAgaGF2ZSBjb252ZXJnZWQsIGZvciBtYWxlcyBlc3BlY2lhbGx5LiANCkFuZCB0aGF0IHRoZXkndmUgbGV2ZWxsZWQgb2ZmIGluIEphcGFuIG92ZXIgdGhlIDIwMDBzLCB3aGlsZSBjb250aW51ZWQgdG8gaW1wcm92ZSBpbiB0aGUgVVNBLiANCg0KSW4gRW5nbGFuZC9XYWxlcyB0aGV5IE14IGF0IGFnZSA5MCBzZWVtIHRvIGhhdmUgaW5jcmVhc2VkIGZvciBmZW1hbGVzIGVzcGVjaWFsbHkgaW4gcmVjZW50IHllYXJzLiANCg0KTGV0J3MgbG9vayBhdCB0aGlzIGZvciB0aGUgYXZlcmFnZSBvZiB0aGUgMjEgY291bnRyaWVzIA0KDQpOb3cgbGV0J3MgZG8gdGhpcyBmb3IgdGhlIGF2ZXJhZ2Ugb2YgdGhlIDIxIGhpZ2ggaW5jb21lIGNvdW50cmllcyANCg0KYGBge3J9DQoNCmR0YV9NeCAlPiUgDQogIGZpbHRlcihzZXggIT0gInRvdGFsIikgJT4lIA0KICBmaWx0ZXIoYWdlICVpbiUgYyg4MCwgOTApKSAlPiUgDQogIGZpbHRlcihjb2RlICVpbiUgaGlnaF9pbmNvbWVfY291bnRyaWVzKSAlPiUgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTAsIDIwMTYpKSAlPiUgDQogIGdyb3VwX2J5KHllYXIsIGFnZSwgc2V4KSAlPiUgDQogIHN1bW1hcmlzZShtZWFuX014ID0gbWVhbihNeCwgbmEucm0gPSBUKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gbWVhbl9NeCwgY29sb3VyID0gc2V4KSkgKyANCiAgZmFjZXRfd3JhcCh+YWdlKSArDQogIGdlb21fcG9pbnQoKQ0KDQoNCmBgYA0KDQpMZXQncyBub3cgdXNlIGxvZyByYXRoZXIgdGhhbiBsaW5lYXIgDQoNCmBgYHtyfQ0KZHRhX014ICU+JSANCiAgZmlsdGVyKHNleCAhPSAidG90YWwiKSAlPiUgDQogIGZpbHRlcihhZ2UgJWluJSBjKDgwLCA5MCkpICU+JSANCiAgZmlsdGVyKGNvZGUgJWluJSBoaWdoX2luY29tZV9jb3VudHJpZXMpICU+JSANCiAgZmlsdGVyKGJldHdlZW4oeWVhciwgMTk1MCwgMjAxNikpICU+JSANCiAgZ3JvdXBfYnkoeWVhciwgYWdlLCBzZXgpICU+JSANCiAgc3VtbWFyaXNlKG1lYW5fTXggPSBtZWFuKE14LCBuYS5ybSA9IFQpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBtZWFuX014LCBjb2xvdXIgPSBzZXgpKSArIA0KICBmYWNldF93cmFwKH5hZ2UpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgc2NhbGVfeV9sb2cxMCgpDQoNCg0KYGBgDQoNClNvLCB0aGUgdHJlbmRzIG9uICUgcmVkdWN0aW9ucyBoYXZlIGJlZW4gbW9yZSBjb250aW51b3VzIGZvciBhZ2UgOTAgdGhhbiBhZ2UgODAsIGFuZCBtb3JlIGNvbnRpbnVvdXMgZm9yIGZlbWFsZXMgdGhhbiBtYWxlcy4gDQoNCkxldCdzIG5vdyBkbyB0aGlzIGZvciBhIGNvdXBsZSBvZiBhZGRpdGlvbmFsIGFnZSBncm91cHMgDQoNCmBgYHtyfQ0KZHRhX014ICU+JSANCiAgZmlsdGVyKHNleCAhPSAidG90YWwiKSAlPiUgDQogIGZpbHRlcihhZ2UgJWluJSBjKDAsIDMwLCA4MCwgOTApKSAlPiUgDQogIGZpbHRlcihjb2RlICVpbiUgaGlnaF9pbmNvbWVfY291bnRyaWVzKSAlPiUgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTAsIDIwMTYpKSAlPiUgDQogIGdyb3VwX2J5KHllYXIsIGFnZSwgc2V4KSAlPiUgDQogIHN1bW1hcmlzZShtZWFuX014ID0gbWVhbihNeCwgbmEucm0gPSBUKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gbWVhbl9NeCwgY29sb3VyID0gc2V4KSkgKyANCiAgZmFjZXRfd3JhcCh+YWdlKSArDQogIGdlb21fcG9pbnQoKSArDQogIHNjYWxlX3lfbG9nMTAoKQ0KDQoNCmBgYA0KDQpOb3cgbGV0J3MgZXN0aW1hdGUgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlc2UgdHJlbmRzDQoNCmBgYHtyfQ0KZHRhX3RybmQgPC0gZHRhX014ICU+JSANCiAgZmlsdGVyKHNleCA9PSAidG90YWwiKSAlPiUgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTUsIDIwMTYpKSAgJT4lIA0KICBmaWx0ZXIoYWdlICVpbiUgYygwLCAzMCwgODAsIDkwKSkgJT4lIA0KICBncm91cF9ieSh5ZWFyLCBhZ2UpICU+JSANCiAgc3VtbWFyaXNlKG1lYW5fTXggPSBtZWFuKE14LCBuYS5ybSA9IFQpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShsb2dfbWVhbl9NeCA9IGxvZyhtZWFuX014LCAxMCkpIA0KDQpkdGFfdHJuZCAlPiUgDQogIHNlbGVjdCgtbWVhbl9NeCkgJT4lIA0KICBtdXRhdGUoYWdlID0gcGFzdGUwKCJhZ2VfIiwgYWdlKSkgJT4lIA0KICBzcHJlYWQoYWdlLCBsb2dfbWVhbl9NeCkgJT4lDQogIHNlbGVjdCgteWVhcikgJT4lIA0KICBjb3IoKQ0KDQpgYGANCg0KU28sIGFzIGV4cGVjdGVkLCAlIGltcHJvdmVtZW50cyBpbiBpbmZhbmN5IGFyZSBtb3JlIHN0cm9uZ2x5IGNvcnJlbGF0ZWQgd2l0aCB0aG9zZSBhdCBhZ2UgODAgYW5kIDkwIHRoYW4gYXQgYWdlIDMwLCBhbmQgJSBjaGFuZ2VzIGF0IGFnZSA5MCBhcmUgbGVzcyBzdHJvbmdseSBjb3JyZWxhdGVkIHRoYW4gd2l0aCB0aG9zZSBhdCBvdGhlciBhZ2VzLiANCg0KTGV0J3MgZG8gdGhpcyBhcyBhIGhlYXRtYXAgZm9yIGFsbCBhZ2VzIA0KDQpgYGB7cn0NCmR0YV90cm5kIDwtIGR0YV9NeCAlPiUgDQogIGZpbHRlcihzZXggPT0gInRvdGFsIikgJT4lIA0KICBmaWx0ZXIoYmV0d2Vlbih5ZWFyLCAxOTU1LCAyMDE2KSkgICU+JSANCiAgZmlsdGVyKGFnZSA8PSAxMDkpICU+JSANCiAgZ3JvdXBfYnkoeWVhciwgYWdlKSAlPiUgDQogIHN1bW1hcmlzZShtZWFuX014ID0gbWVhbihNeCwgbmEucm0gPSBUKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBtdXRhdGUobG9nX21lYW5fTXggPSBsb2cobWVhbl9NeCwgMTApKSANCg0KdG1wIDwtIGR0YV90cm5kICU+JSANCiAgc2VsZWN0KC1tZWFuX014KSAlPiUgDQogIHNwcmVhZChhZ2UsIGxvZ19tZWFuX014KSAlPiUNCiAgc2VsZWN0KC15ZWFyKSAlPiUgDQogIGNvcigpIA0KDQpjb3JfZGYgPC0gdG1wICU+JSANCiAgYXNfdGliYmxlKCkgJT4lIA0KICBtdXRhdGUoZnJvbV9hZ2UgPSByb3duYW1lcyh0bXApKSAlPiUgDQogIGdhdGhlcihrZXk9InRvX2FnZSIsIHZhbHVlID0gInZhbHVlIiwgLWZyb21fYWdlKSAlPiUgDQogIG11dGF0ZShmcm9tX2FnZSA9IGFzLm51bWVyaWMoZnJvbV9hZ2UpLCB0b19hZ2UgPSBhcy5udW1lcmljKHRvX2FnZSkpDQoNCmNvcl9kZiAlPiUgDQogIGZpbHRlcihmcm9tX2FnZSA8PSAxMDAsIHRvX2FnZSA8PSAxMDApICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZnJvbV9hZ2UsIHkgPSB0b19hZ2UsIGZpbGwgPSB2YWx1ZSkpICsgDQogIGdlb21fdGlsZSgpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgY29vcmRfZXF1YWwoKQ0KDQpgYGANCg0KDQpEZWZpbml0ZWx5IGEgJ3dvdycgZmlndXJlISANCg0KSW50ZXJlc3RpbmcgdGhhdCB0aGUgdHJlbmRzIHRoYXQgYXBwbHkgYXQgb2xkZXIgYWdlcyBkb24ndCBhcHBseSBhdCB0aGUgb2xkZXN0IGFnZXMgKGZyb20gYXJvdW5kIGFnZSA5NiBvbndhcmRzKQ0KDQpMZXQncyBzZWUgaG93IGRpZmZlcmVudCBpdCBsb29rcyBieSBnZW5kZXIuDQoNCmBgYHtyfQ0KZHRhX3RybmQgPC0gZHRhX014ICU+JSANCiAgZmlsdGVyKHNleCAhPSAidG90YWwiKSAlPiUgDQogIGZpbHRlcihiZXR3ZWVuKHllYXIsIDE5NTUsIDIwMTYpKSAgJT4lIA0KICBmaWx0ZXIoYWdlIDw9IDEwOSkgJT4lIA0KICBncm91cF9ieShzZXgsIHllYXIsIGFnZSkgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9NeCA9IG1lYW4oTXgsIG5hLnJtID0gVCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKGxvZ19tZWFuX014ID0gbG9nKG1lYW5fTXgsIDEwKSkgJT4lIA0KICBncm91cF9ieShzZXgpICU+JSANCiAgbmVzdCgpDQoNCg0KY29yc19kZiA8LSBkdGFfdHJuZCAlPiUgDQogIG11dGF0ZShjb3JzID0gbWFwKA0KICAgIGRhdGEsIA0KICAgIGZ1bmN0aW9uKFgpIHsNCiAgICAgIFggJT4lIA0KICAgICAgICBzZWxlY3QoLW1lYW5fTXgpICU+JSANCiAgICAgICAgc3ByZWFkKGFnZSwgbG9nX21lYW5fTXgpICU+JSANCiAgICAgICAgc2VsZWN0KC15ZWFyKSAlPiUgDQogICAgICAgIGNvcigpDQogICAgICB9DQogICAgKQ0KICApICU+JSANCiAgbXV0YXRlKGNvcl9kZiA9IG1hcCgNCiAgICBjb3JzLA0KICAgIGZ1bmN0aW9uKFgpew0KICAgICAgWCAlPiUgDQogICAgICAgIGFzX3RpYmJsZSgpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gcm93bmFtZXMoWCkpICU+JSANCiAgICAgICAgZ2F0aGVyKGtleSA9ICJ0b19hZ2UiLCB2YWx1ZSA9ICJ2YWx1ZSIsIC1mcm9tX2FnZSkgJT4lIA0KICAgICAgICBtdXRhdGUoZnJvbV9hZ2UgPSBhcy5udW1lcmljKGZyb21fYWdlKSwgdG9fYWdlID0gYXMubnVtZXJpYyh0b19hZ2UpKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIHNlbGVjdChzZXgsIGNvcl9kZikgJT4lIA0KICB1bm5lc3QoKQ0KDQoNCmNvcnNfZGYgJT4lIA0KICBmaWx0ZXIoZnJvbV9hZ2UgPD0gMTAwLCB0b19hZ2UgPD0gMTAwKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IGZyb21fYWdlLCB5ID0gdG9fYWdlLCBmaWxsID0gdmFsdWUpKSArIA0KICBnZW9tX3RpbGUoKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIGNvb3JkX2VxdWFsKCkgKyANCiAgZmFjZXRfd3JhcCh+c2V4KQ0KDQpgYGANCg0KQWdhaW4sIGJlYXV0aWZ1bCwgc3RhZ2dlcmluZywgYW5kIGF3ZXNvbWUuIFRoaXMgaXMgbGlrZSBzZWVpbmcgdGhlIGdlbm9tZSBvZiBwb3B1bGF0aW9uIGhlYWx0aCBpbXByb3ZlbWVudCBiZWluZyBtYXBwZWQuIFRoZSBnZW5kZXIgZGlmZmVyZW5jZXMgaW4gdGhlIGRpZmZlcmVuY2VzIGluIGNvcnJlbGF0aW9uIGFyZSB2ZXJ5IGFwcGFyZW50LiANCg0KTGV0J3MgZXh0ZW5kIHRoaXMgdG8gYSBmZXcgZmV3IHNlbGVjdCBjb3VudHJpZXM6IA0KDQoqIEZyYW5jZQ0KKiBTd2VkZW4/DQoqIFVLDQoqIFVTQSANCiogU3BhaW4gKElkZW50aWZpZWQgYXMgbW9zdCBjb21wYXRpYmxlIHdpdGggTGVlLUNhcnRlciBtb2RlbGxpbmcgYXBwcm9hY2gpDQoqIEphcGFuDQoNCg0KYGBge3J9DQpkdGFfdHJuZCA8LSBkdGFfTXggJT4lIA0KICBmaWx0ZXIoc2V4ICE9ICJ0b3RhbCIpICU+JQ0KICBmaWx0ZXIoY29kZSAlaW4lIGMoIkZSQVROUCIsICJTV0UiLCAiR0JSX05QIiwgIlVTQSIsICJFU1AiLCAiSlBOIikpICU+JSANCiAgZmlsdGVyKGJldHdlZW4oeWVhciwgMTk1NSwgMjAxNikpICAlPiUgDQogIGZpbHRlcihhZ2UgPD0gMTA5KSAlPiUgDQogIGdyb3VwX2J5KGNvZGUsIHNleCwgeWVhciwgYWdlKSAlPiUgDQogIHN1bW1hcmlzZShtZWFuX014ID0gbWVhbihNeCwgbmEucm0gPSBUKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBtdXRhdGUobG9nX21lYW5fTXggPSBsb2cobWVhbl9NeCArIDAuMDAwMDEsIDEwKSkgJT4lICMgQ29ycmVjdGlvbiBmb3IgU3dlZGVuDQogIGdyb3VwX2J5KHNleCwgY29kZSkgJT4lIA0KICBuZXN0KCkNCg0KDQpjb3JzX2RmIDwtIGR0YV90cm5kICU+JSANCiAgbXV0YXRlKGNvcnMgPSBtYXAoDQogICAgZGF0YSwgDQogICAgZnVuY3Rpb24oWCkgew0KICAgICAgWCAlPiUgDQogICAgICAgIHNlbGVjdCgtbWVhbl9NeCkgJT4lIA0KICAgICAgICBzcHJlYWQoYWdlLCBsb2dfbWVhbl9NeCkgJT4lIA0KICAgICAgICBzZWxlY3QoLXllYXIpICU+JSANCiAgICAgICAgY29yKCkNCiAgICAgIH0NCiAgICApDQogICkgJT4lIA0KICBtdXRhdGUoY29yX2RmID0gbWFwKA0KICAgIGNvcnMsDQogICAgZnVuY3Rpb24oWCl7DQogICAgICBYICU+JSANCiAgICAgICAgYXNfdGliYmxlKCkgJT4lIA0KICAgICAgICBtdXRhdGUoZnJvbV9hZ2UgPSByb3duYW1lcyhYKSkgJT4lIA0KICAgICAgICBnYXRoZXIoa2V5ID0gInRvX2FnZSIsIHZhbHVlID0gInZhbHVlIiwgLWZyb21fYWdlKSAlPiUgDQogICAgICAgIG11dGF0ZShmcm9tX2FnZSA9IGFzLm51bWVyaWMoZnJvbV9hZ2UpLCB0b19hZ2UgPSBhcy5udW1lcmljKHRvX2FnZSkpDQogICAgICB9DQogICAgKQ0KICApICU+JSANCiAgc2VsZWN0KHNleCwgY29kZSwgY29yX2RmKSAlPiUgDQogIHVubmVzdCgpDQoNCg0KY29yc19kZiAlPiUgDQogIGZpbHRlcihmcm9tX2FnZSA8PSAxMDAsIHRvX2FnZSA8PSAxMDApICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZnJvbV9hZ2UsIHkgPSB0b19hZ2UsIGZpbGwgPSB2YWx1ZSkpICsgDQogIGdlb21fdGlsZSgpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MobGltaXRzID0gYygtMSwxKSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIGNvb3JkX2VxdWFsKCkgKyANCiAgZmFjZXRfZ3JpZChzZXggfiBjb2RlKQ0KDQpgYGANCg0KDQpMZXQncyBkbyB0aGlzIGp1c3QgZm9yIHRoZSBVSyBuYXRpb25zIA0KDQpgYGB7cn0NCmR0YV90cm5kIDwtIGR0YV9NeCAlPiUgDQogIGZpbHRlcihzZXggIT0gInRvdGFsIikgJT4lDQogIGZpbHRlcihjb2RlICVpbiUgYygiR0JSVEVOVyIsICJHQlJfU0NPIiwgIkdCUl9OSVIiKSkgJT4lIA0KICBmaWx0ZXIoYmV0d2Vlbih5ZWFyLCAxOTU1LCAyMDE2KSkgICU+JSANCiAgZmlsdGVyKGFnZSA8PSAxMDkpICU+JSANCiAgZ3JvdXBfYnkoY29kZSwgc2V4LCB5ZWFyLCBhZ2UpICU+JSANCiAgc3VtbWFyaXNlKG1lYW5fTXggPSBtZWFuKE14LCBuYS5ybSA9IFQpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShsb2dfbWVhbl9NeCA9IGxvZyhtZWFuX014ICsgMC4wMDAwMSwgMTApKSAlPiUgIyBDb3JyZWN0aW9uIGZvciBTd2VkZW4NCiAgZ3JvdXBfYnkoc2V4LCBjb2RlKSAlPiUgDQogIG5lc3QoKQ0KDQoNCmNvcnNfZGYgPC0gZHRhX3RybmQgJT4lIA0KICBtdXRhdGUoY29ycyA9IG1hcCgNCiAgICBkYXRhLCANCiAgICBmdW5jdGlvbihYKSB7DQogICAgICBYICU+JSANCiAgICAgICAgc2VsZWN0KC1tZWFuX014KSAlPiUgDQogICAgICAgIHNwcmVhZChhZ2UsIGxvZ19tZWFuX014KSAlPiUgDQogICAgICAgIHNlbGVjdCgteWVhcikgJT4lIA0KICAgICAgICBjb3IoKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIG11dGF0ZShjb3JfZGYgPSBtYXAoDQogICAgY29ycywNCiAgICBmdW5jdGlvbihYKXsNCiAgICAgIFggJT4lIA0KICAgICAgICBhc190aWJibGUoKSAlPiUgDQogICAgICAgIG11dGF0ZShmcm9tX2FnZSA9IHJvd25hbWVzKFgpKSAlPiUgDQogICAgICAgIGdhdGhlcihrZXkgPSAidG9fYWdlIiwgdmFsdWUgPSAidmFsdWUiLCAtZnJvbV9hZ2UpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gYXMubnVtZXJpYyhmcm9tX2FnZSksIHRvX2FnZSA9IGFzLm51bWVyaWModG9fYWdlKSkNCiAgICAgIH0NCiAgICApDQogICkgJT4lIA0KICBzZWxlY3Qoc2V4LCBjb2RlLCBjb3JfZGYpICU+JSANCiAgdW5uZXN0KCkNCg0KDQpjb3JzX2RmICU+JSANCiAgZmlsdGVyKGZyb21fYWdlIDw9IDEwMCwgdG9fYWdlIDw9IDEwMCkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBmcm9tX2FnZSwgeSA9IHRvX2FnZSwgZmlsbCA9IHZhbHVlKSkgKyANCiAgZ2VvbV90aWxlKCkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhsaW1pdHMgPSBjKC0xLDEpKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgY29vcmRfZXF1YWwoKSArIA0KICBmYWNldF9ncmlkKHNleCB+Y29kZSkgKyANCiAgbGFicyh4ID0gIkFnZSIsIHkgPSAiQWdlIiwgdGl0bGUgPSAiQ29ycmVsYXRpb25zIGJldHdlZW4gcmF0ZXMgb2YgY2hhbmdlIGluIGxvZyBtb3J0YWxpdHkgcmlzayBhZ2UgZGlmZmVyZW50IGFnZXMiLA0KICAgICAgIHN1YnRpdGxlID0gIlRyZW5kcyBmcm9tIDE5NTUtMjAxNi4gRW5nbGFuZCAmIFdhbGVzLCBTY290bGFuZCwgTm9ydGhlcm4gSXJlbGFuZCIsDQogICAgICAgY2FwdGlvbiA9ICJTb3VyY2U6IEh1bWFuIE1vcnRhbGl0eSBEYXRhYmFzZS4gQXZhaWxhYmxlIGZyb20gaHR0cHM6Ly9naXRodWIuY29tL0pvbk1pbnRvbi9MaWZlX2V4cGVjdGFuY3lfbGltaXRzLyIpDQoNCmdnc2F2ZSgiZmlndXJlcy90cmVuZF9jb3JyZWxhdGlvbl9oZWF0bWFwLnBuZyIsIGhlaWdodCA9IDIwLCB3aWR0aCA9IDI1LCB1bml0cyA9ICJjbSIsIGRwaSA9IDMwMCkNCg0KDQpgYGANCg0KDQpBbHRob3VnaCBwbG90bHkgZG9lc24ndCBjb252ZXJ0IGZhY2V0aW5nIHRvbyBlYXNpbHksIGNhbiBJIHVzZSBnZ3Bsb3RseSBmb3IgaG92ZXJpbmcgb3ZlciB2YWx1ZXMgZm9yIFNjb3RsYW5kIG1hbGVzIGFsb25lPw0KDQpgYGB7cn0NCnAgPC0gY29yc19kZiAlPiUgDQogIGZpbHRlcihmcm9tX2FnZSA8PSAxMDAsIHRvX2FnZSA8PSAxMDApICU+JSANCiAgZmlsdGVyKHNleCA9PSAibWFsZSIpICU+JSANCiAgZmlsdGVyKGNvZGUgPT0gIkdCUl9TQ08iKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IGZyb21fYWdlLCB5ID0gdG9fYWdlLCBmaWxsID0gdmFsdWUpKSArIA0KICBnZW9tX3RpbGUoKSArIA0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpICsgDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsgDQogIGNvb3JkX2VxdWFsKCkgDQoNCnAgPC0gY29yc19kZiAlPiUgDQogIGZpbHRlcihmcm9tX2FnZSA8PSAxMDAsIHRvX2FnZSA8PSAxMDApICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZnJvbV9hZ2UsIHkgPSB0b19hZ2UsIGZpbGwgPSB2YWx1ZSkpICsgDQogIGdlb21fdGlsZSgpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgY29vcmRfZXF1YWwoKSArIA0KICBmYWNldF9ncmlkKHNleCB+Y29kZSkNCg0KcGxvdGx5OjpnZ3Bsb3RseShwKQ0KDQpgYGANCg0KZ2dwbG90bHkgbm93IHdvcmtzIHdpdGggZmFjZXRlZCBwbG90cyEgKFRob3VnaCB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBsb29rcyBsaWtlIGl0IGNhdXNlcyB0aGluZ3MgdG8gc3RydWdnbGUhKQ0KDQpGb3IgU2NvdGxhbmQgYW5kIEVuZ2xhbmQgJiBXYWxlcyAoaW5kZXBlbmRlbnRseSksIGhvdyBoYXZlIHRoZSBjb3JyZWxhdGlvbnMgY2hhbmdlZCBvdmVyIHRpbWU/IA0KDQpFbmdsYW5kL1dhbGVzIGZpcnN0OiANCg0KYGBge3J9DQpkdGFfdHJuZCA8LSBkdGFfTXggJT4lIA0KICBmaWx0ZXIoc2V4ICE9ICJ0b3RhbCIpICU+JQ0KICBmaWx0ZXIoY29kZSAlaW4lIGMoIkdCUlRFTlciKSkgJT4lIA0KICBmaWx0ZXIoYmV0d2Vlbih5ZWFyLCAxOTUwLCAyMDEwKSkgICU+JQ0KICBmaWx0ZXIoYWdlIDw9IDEwOSkgJT4lIA0KICBtdXRhdGUoDQogICAgZGVjYWRlID0gY3V0KHllYXIsIGJyZWFrcyA9IHNlcSgxOTUwLCAyMDEwLCBieSA9IDEwKSwgbGFiZWxzID0gYygiMTk1MHMiLCAiMTk2MHMiLCAiMTk3MHMiLCAiMTk4MHMiLCAiMTk5MHMiLCAiMjAwMHMiKSwgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKQ0KICApICU+JSANCiAgZ3JvdXBfYnkoc2V4LCBkZWNhZGUsIHllYXIsIGFnZSkgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9NeCA9IG1lYW4oTXgsIG5hLnJtID0gVCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKGxvZ19tZWFuX014ID0gbG9nKG1lYW5fTXgsIDEwKSkgJT4lICMgQ29ycmVjdGlvbiBmb3IgU3dlZGVuDQogIGdyb3VwX2J5KHNleCwgZGVjYWRlKSAlPiUgDQogIG5lc3QoKQ0KDQoNCmNvcnNfZGYgPC0gZHRhX3RybmQgJT4lIA0KICBtdXRhdGUoY29ycyA9IG1hcCgNCiAgICBkYXRhLCANCiAgICBmdW5jdGlvbihYKSB7DQogICAgICBYICU+JSANCiAgICAgICAgc2VsZWN0KC1tZWFuX014KSAlPiUgDQogICAgICAgIHNwcmVhZChhZ2UsIGxvZ19tZWFuX014KSAlPiUgDQogICAgICAgIHNlbGVjdCgteWVhcikgJT4lIA0KICAgICAgICBjb3IoKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIG11dGF0ZShjb3JfZGYgPSBtYXAoDQogICAgY29ycywNCiAgICBmdW5jdGlvbihYKXsNCiAgICAgIFggJT4lIA0KICAgICAgICBhc190aWJibGUoKSAlPiUgDQogICAgICAgIG11dGF0ZShmcm9tX2FnZSA9IHJvd25hbWVzKFgpKSAlPiUgDQogICAgICAgIGdhdGhlcihrZXkgPSAidG9fYWdlIiwgdmFsdWUgPSAidmFsdWUiLCAtZnJvbV9hZ2UpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gYXMubnVtZXJpYyhmcm9tX2FnZSksIHRvX2FnZSA9IGFzLm51bWVyaWModG9fYWdlKSkNCiAgICAgIH0NCiAgICApDQogICkgJT4lIA0KICBzZWxlY3Qoc2V4LCBkZWNhZGUsIGNvcl9kZikgJT4lIA0KICB1bm5lc3QoKQ0KDQoNCmNvcnNfZGYgJT4lIA0KICBmaWx0ZXIoZnJvbV9hZ2UgPD0gMTAwLCB0b19hZ2UgPD0gMTAwKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IGZyb21fYWdlLCB5ID0gdG9fYWdlLCBmaWxsID0gdmFsdWUpKSArIA0KICBnZW9tX3RpbGUoKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIGNvb3JkX2VxdWFsKCkgKyANCiAgZmFjZXRfZ3JpZChzZXggfiBkZWNhZGUpDQoNCmBgYA0KDQoNCg0KTm93IGZvciBTY290bGFuZC4gDQoNCmBgYHtyfQ0KZHRhX3RybmQgPC0gZHRhX014ICU+JSANCiAgZmlsdGVyKHNleCAhPSAidG90YWwiKSAlPiUNCiAgZmlsdGVyKGNvZGUgJWluJSBjKCJHQlJfU0NPIikpICU+JSANCiAgZmlsdGVyKGJldHdlZW4oeWVhciwgMTk1MCwgMjAxMCkpICAlPiUNCiAgZmlsdGVyKGFnZSA8PSAxMDkpICU+JSANCiAgbXV0YXRlKA0KICAgIGRlY2FkZSA9IGN1dCh5ZWFyLCBicmVha3MgPSBzZXEoMTk1MCwgMjAxMCwgYnkgPSAxMCksIGxhYmVscyA9IGMoIjE5NTBzIiwgIjE5NjBzIiwgIjE5NzBzIiwgIjE5ODBzIiwgIjE5OTBzIiwgIjIwMDBzIiksIGluY2x1ZGUubG93ZXN0ID0gVFJVRSkNCiAgKSAlPiUgDQogIGdyb3VwX2J5KHNleCwgZGVjYWRlLCB5ZWFyLCBhZ2UpICU+JSANCiAgc3VtbWFyaXNlKG1lYW5fTXggPSBtZWFuKE14LCBuYS5ybSA9IFQpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShsb2dfbWVhbl9NeCA9IGxvZyhtZWFuX014ICsgMC4wMDAwMSwgMTApKSAlPiUgIyBDb3JyZWN0aW9uIGZvciBTd2VkZW4NCiAgZ3JvdXBfYnkoc2V4LCBkZWNhZGUpICU+JSANCiAgbmVzdCgpDQoNCg0KY29yc19kZiA8LSBkdGFfdHJuZCAlPiUgDQogIG11dGF0ZShjb3JzID0gbWFwKA0KICAgIGRhdGEsIA0KICAgIGZ1bmN0aW9uKFgpIHsNCiAgICAgIFggJT4lIA0KICAgICAgICBzZWxlY3QoLW1lYW5fTXgpICU+JSANCiAgICAgICAgc3ByZWFkKGFnZSwgbG9nX21lYW5fTXgpICU+JSANCiAgICAgICAgc2VsZWN0KC15ZWFyKSAlPiUgDQogICAgICAgIGNvcigpDQogICAgICB9DQogICAgKQ0KICApICU+JSANCiAgbXV0YXRlKGNvcl9kZiA9IG1hcCgNCiAgICBjb3JzLA0KICAgIGZ1bmN0aW9uKFgpew0KICAgICAgWCAlPiUgDQogICAgICAgIGFzX3RpYmJsZSgpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gcm93bmFtZXMoWCkpICU+JSANCiAgICAgICAgZ2F0aGVyKGtleSA9ICJ0b19hZ2UiLCB2YWx1ZSA9ICJ2YWx1ZSIsIC1mcm9tX2FnZSkgJT4lIA0KICAgICAgICBtdXRhdGUoZnJvbV9hZ2UgPSBhcy5udW1lcmljKGZyb21fYWdlKSwgdG9fYWdlID0gYXMubnVtZXJpYyh0b19hZ2UpKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIHNlbGVjdChzZXgsIGRlY2FkZSwgY29yX2RmKSAlPiUgDQogIHVubmVzdCgpDQoNCg0KY29yc19kZiAlPiUgDQogIGZpbHRlcihmcm9tX2FnZSA8PSAxMDAsIHRvX2FnZSA8PSAxMDApICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gZnJvbV9hZ2UsIHkgPSB0b19hZ2UsIGZpbGwgPSB2YWx1ZSkpICsgDQogIGdlb21fdGlsZSgpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgY29vcmRfZXF1YWwoKSArIA0KICBmYWNldF9ncmlkKHNleCB+IGRlY2FkZSkNCg0KYGBgDQpOb3J0aGVybiBJcmVsYW5kDQoNCg0KYGBge3J9DQpkdGFfdHJuZCA8LSBkdGFfTXggJT4lIA0KICBmaWx0ZXIoc2V4ICE9ICJ0b3RhbCIpICU+JQ0KICBmaWx0ZXIoY29kZSAlaW4lIGMoIkdCUl9OSVIiKSkgJT4lIA0KICBmaWx0ZXIoYmV0d2Vlbih5ZWFyLCAxOTUwLCAyMDEwKSkgICU+JQ0KICBmaWx0ZXIoYWdlIDw9IDEwOSkgJT4lIA0KICBtdXRhdGUoDQogICAgZGVjYWRlID0gY3V0KHllYXIsIGJyZWFrcyA9IHNlcSgxOTUwLCAyMDEwLCBieSA9IDEwKSwgbGFiZWxzID0gYygiMTk1MHMiLCAiMTk2MHMiLCAiMTk3MHMiLCAiMTk4MHMiLCAiMTk5MHMiLCAiMjAwMHMiKSwgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKQ0KICApICU+JSANCiAgZ3JvdXBfYnkoc2V4LCBkZWNhZGUsIHllYXIsIGFnZSkgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9NeCA9IG1lYW4oTXgsIG5hLnJtID0gVCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKGxvZ19tZWFuX014ID0gbG9nKG1lYW5fTXggKyAwLjAwMDAxLCAxMCkpICU+JSAjIENvcnJlY3Rpb24gZm9yIFN3ZWRlbg0KICBncm91cF9ieShzZXgsIGRlY2FkZSkgJT4lIA0KICBuZXN0KCkNCg0KDQpjb3JzX2RmIDwtIGR0YV90cm5kICU+JSANCiAgbXV0YXRlKGNvcnMgPSBtYXAoDQogICAgZGF0YSwgDQogICAgZnVuY3Rpb24oWCkgew0KICAgICAgWCAlPiUgDQogICAgICAgIHNlbGVjdCgtbWVhbl9NeCkgJT4lIA0KICAgICAgICBzcHJlYWQoYWdlLCBsb2dfbWVhbl9NeCkgJT4lIA0KICAgICAgICBzZWxlY3QoLXllYXIpICU+JSANCiAgICAgICAgY29yKCkNCiAgICAgIH0NCiAgICApDQogICkgJT4lIA0KICBtdXRhdGUoY29yX2RmID0gbWFwKA0KICAgIGNvcnMsDQogICAgZnVuY3Rpb24oWCl7DQogICAgICBYICU+JSANCiAgICAgICAgYXNfdGliYmxlKCkgJT4lIA0KICAgICAgICBtdXRhdGUoZnJvbV9hZ2UgPSByb3duYW1lcyhYKSkgJT4lIA0KICAgICAgICBnYXRoZXIoa2V5ID0gInRvX2FnZSIsIHZhbHVlID0gInZhbHVlIiwgLWZyb21fYWdlKSAlPiUgDQogICAgICAgIG11dGF0ZShmcm9tX2FnZSA9IGFzLm51bWVyaWMoZnJvbV9hZ2UpLCB0b19hZ2UgPSBhcy5udW1lcmljKHRvX2FnZSkpDQogICAgICB9DQogICAgKQ0KICApICU+JSANCiAgc2VsZWN0KHNleCwgZGVjYWRlLCBjb3JfZGYpICU+JSANCiAgdW5uZXN0KCkNCg0KDQpjb3JzX2RmICU+JSANCiAgZmlsdGVyKGZyb21fYWdlIDw9IDEwMCwgdG9fYWdlIDw9IDEwMCkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBmcm9tX2FnZSwgeSA9IHRvX2FnZSwgZmlsbCA9IHZhbHVlKSkgKyANCiAgZ2VvbV90aWxlKCkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBjb29yZF9lcXVhbCgpICsgDQogIGZhY2V0X2dyaWQoc2V4IH4gZGVjYWRlKQ0KDQpgYGANCg0KYGBge3J9DQpkdGFfdHJuZCA8LSBkdGFfTXggJT4lIA0KICBmaWx0ZXIoc2V4ICE9ICJ0b3RhbCIpICU+JQ0KICBmaWx0ZXIoY29kZSAlaW4lIGMoIkdCUlRFTlciKSkgJT4lIA0KICBmaWx0ZXIoYmV0d2Vlbih5ZWFyLCAxOTU1LCAyMDE1KSkgICU+JQ0KICBmaWx0ZXIoYWdlIDw9IDEwOSkgJT4lIA0KICBtdXRhdGUoDQogICAgZGVjYWRlID0gY3V0KHllYXIsIGJyZWFrcyA9IHNlcSgxOTU1LCAyMDE1LCBieSA9IDEwKSwgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKQ0KICApICU+JSANCiAgZ3JvdXBfYnkoc2V4LCBkZWNhZGUsIHllYXIsIGFnZSkgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9NeCA9IG1lYW4oTXgsIG5hLnJtID0gVCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKGxvZ19tZWFuX014ID0gbG9nKG1lYW5fTXgsIDEwKSkgJT4lICMgQ29ycmVjdGlvbiBmb3IgU3dlZGVuDQogIGdyb3VwX2J5KHNleCwgZGVjYWRlKSAlPiUgDQogIG5lc3QoKQ0KDQoNCmNvcnNfZGYgPC0gZHRhX3RybmQgJT4lIA0KICBtdXRhdGUoY29ycyA9IG1hcCgNCiAgICBkYXRhLCANCiAgICBmdW5jdGlvbihYKSB7DQogICAgICBYICU+JSANCiAgICAgICAgc2VsZWN0KC1tZWFuX014KSAlPiUgDQogICAgICAgIHNwcmVhZChhZ2UsIGxvZ19tZWFuX014KSAlPiUgDQogICAgICAgIHNlbGVjdCgteWVhcikgJT4lIA0KICAgICAgICBjb3IoKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgDQogIG11dGF0ZShjb3JfZGYgPSBtYXAoDQogICAgY29ycywNCiAgICBmdW5jdGlvbihYKXsNCiAgICAgIFggJT4lIA0KICAgICAgICBhc190aWJibGUoKSAlPiUgDQogICAgICAgIG11dGF0ZShmcm9tX2FnZSA9IHJvd25hbWVzKFgpKSAlPiUgDQogICAgICAgIGdhdGhlcihrZXkgPSAidG9fYWdlIiwgdmFsdWUgPSAidmFsdWUiLCAtZnJvbV9hZ2UpICU+JSANCiAgICAgICAgbXV0YXRlKGZyb21fYWdlID0gYXMubnVtZXJpYyhmcm9tX2FnZSksIHRvX2FnZSA9IGFzLm51bWVyaWModG9fYWdlKSkNCiAgICAgIH0NCiAgICApDQogICkgJT4lIA0KICBzZWxlY3Qoc2V4LCBkZWNhZGUsIGNvcl9kZikgJT4lIA0KICB1bm5lc3QoKQ0KDQoNCmNvcnNfZGYgJT4lIA0KICBmaWx0ZXIoZnJvbV9hZ2UgPD0gMTAwLCB0b19hZ2UgPD0gMTAwKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IGZyb21fYWdlLCB5ID0gdG9fYWdlLCBmaWxsID0gdmFsdWUpKSArIA0KICBnZW9tX3RpbGUoKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEwMCwgYnkgPSAxMCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIGNvb3JkX2VxdWFsKCkgKyANCiAgZmFjZXRfZ3JpZChzZXggfiBkZWNhZGUpICsNCiAgbGFicygNCiAgICB4ID0gIkFnZSIsIHkgPSAiQWdlIiwNCiAgICB0aXRsZSA9ICJDb3JyZWxhdGlvbiBiZXR3ZWVuIHRyZW5kcyBhdCBpbmRpdmR1YWwgYWdlcywgRW5nbGFuZCAmIFdhbGVzLCBieSBkZWNhZGUiDQogICkNCg0KYGBgDQoNCg0KDQpOb3cgZm9yIFNjb3RsYW5kLiANCg0KYGBge3J9DQpkdGFfdHJuZCA8LSBkdGFfTXggJT4lIA0KICBmaWx0ZXIoc2V4ICE9ICJ0b3RhbCIpICU+JQ0KICBmaWx0ZXIoY29kZSAlaW4lIGMoIkdCUl9TQ08iKSkgJT4lIA0KICBmaWx0ZXIoYmV0d2Vlbih5ZWFyLCAxOTU1LCAyMDE1KSkgICU+JQ0KICBmaWx0ZXIoYWdlIDw9IDEwOSkgJT4lIA0KICBtdXRhdGUoDQogICAgZGVjYWRlID0gY3V0KHllYXIsIGJyZWFrcyA9IHNlcSgxOTU1LCAyMDE1LCBieSA9IDEwKSwgaW5jbHVkZS5sb3dlc3QgPSBUUlVFKQ0KICApICU+JSANCiAgZ3JvdXBfYnkoc2V4LCBkZWNhZGUsIHllYXIsIGFnZSkgJT4lIA0KICBzdW1tYXJpc2UobWVhbl9NeCA9IG1lYW4oTXgsIG5hLnJtID0gVCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKGxvZ19tZWFuX014ID0gbG9nKG1lYW5fTXggKyAwLjAwMDAxLCAxMCkpICU+JSAjIENvcnJlY3Rpb24gZm9yIFN3ZWRlbg0KICBncm91cF9ieShzZXgsIGRlY2FkZSkgJT4lIA0KICBuZXN0KCkNCg0KDQpjb3JzX2RmIDwtIGR0YV90cm5kICU+JSANCiAgbXV0YXRlKGNvcnMgPSBtYXAoDQogICAgZGF0YSwgDQogICAgZnVuY3Rpb24oWCkgew0KICAgICAgWCAlPiUgDQogICAgICAgIHNlbGVjdCgtbWVhbl9NeCkgJT4lIA0KICAgICAgICBzcHJlYWQoYWdlLCBsb2dfbWVhbl9NeCkgJT4lIA0KICAgICAgICBzZWxlY3QoLXllYXIpICU+JSANCiAgICAgICAgY29yKCkNCiAgICAgIH0NCiAgICApDQogICkgJT4lIA0KICBtdXRhdGUoY29yX2RmID0gbWFwKA0KICAgIGNvcnMsDQogICAgZnVuY3Rpb24oWCl7DQogICAgICBYICU+JSANCiAgICAgICAgYXNfdGliYmxlKCkgJT4lIA0KICAgICAgICBtdXRhdGUoZnJvbV9hZ2UgPSByb3duYW1lcyhYKSkgJT4lIA0KICAgICAgICBnYXRoZXIoa2V5ID0gInRvX2FnZSIsIHZhbHVlID0gInZhbHVlIiwgLWZyb21fYWdlKSAlPiUgDQogICAgICAgIG11dGF0ZShmcm9tX2FnZSA9IGFzLm51bWVyaWMoZnJvbV9hZ2UpLCB0b19hZ2UgPSBhcy5udW1lcmljKHRvX2FnZSkpDQogICAgICB9DQogICAgKQ0KICApICU+JSANCiAgc2VsZWN0KHNleCwgZGVjYWRlLCBjb3JfZGYpICU+JSANCiAgdW5uZXN0KCkNCg0KDQpjb3JzX2RmICU+JSANCiAgZmlsdGVyKGZyb21fYWdlIDw9IDEwMCwgdG9fYWdlIDw9IDEwMCkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBmcm9tX2FnZSwgeSA9IHRvX2FnZSwgZmlsbCA9IHZhbHVlKSkgKyANCiAgZ2VvbV90aWxlKCkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxMDAsIGJ5ID0gMTApKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMTAwLCBieSA9IDEwKSkgKw0KICBjb29yZF9lcXVhbCgpICsgDQogIGZhY2V0X2dyaWQoc2V4IH4gZGVjYWRlKQ0KDQpgYGANCg0KIyMgQW5hbHlzaXMvcmVzdWx0cyBmb3IgYXFtZW4gY29uZmVyZW5jZSBwcmVzZW50YXRpb24NCg0KUG9wdWxhdGlvbnM6DQoNCiogRnJhbmNlLCANCiogVUsNCiogVVNBDQoqIEphcGFuDQoNClRpbWUgcGVyaW9kOg0KDQoqIDE5NzAgdG8gbGFzdCBhdmFpbGFibGUgeWVhcg0KDQpBbmFseXNlczoNCg0KKiBJbmRpdmlkdWFsIHBsb3RzDQoqIENvcnJlbGF0aW9ucyBiZXR3ZWVuIGJyb2FkIGFnZSBncm91cHMgDQoqIERlbmRyb2dyYW0gb2YgYWdlIGNvcnJlbGF0aW9ucyANCg0KDQpgYGB7cn0NCg0KZHRhX014ICU+JSANCiAgZmlsdGVyKGNvZGUgJWluJSBjKCJGUkFUTlAiLCAiR0JSX05QIiwgIlVTQSIsICJKUE4iKSkgJT4lIA0KICBmaWx0ZXIoeWVhciA+PSAxOTcwKSAlPiUgDQogIGZpbHRlcihhZ2UgPD0gMTA5KSAlPiUgDQogIGZpbHRlcihzZXggIT0gInRvdGFsIikgJT4lIA0KICBncm91cF9ieShjb2RlLCBzZXgpICU+JSANCiAgbmVzdCgpIA0KDQoNCmBgYA0KDQoNCkxldCdzIGxvb2sgYXQgaGNsdXN0IGV4YW1wbGVzIA0KDQpJdCBzZWVtcyBmcm9tIHRoaXMgdGhhdCB0aGUgZGF0YXN0cnVjdHVyZSByZXF1aXJlZCBzaG91bGQgaGF2ZSB0aGUgZm9sbG93aW5nOg0KDQoqIHJvd25hbWVzOiB0aGUgaW5kaXZpZHVhbCBhZ2VzDQoqIGNvbHVtbnM6IHJhdGUgaW4gZGlmZmVyZW50IHllYXJzIA0KDQpMZXQncyBzdGFydCB3aXRoIGEgc2luZ2xlIHBvcHVsYXRpb246IFVTQSBtYWxlcw0KDQpgYGB7cn0NCmR0YV9NeCAlPiUgDQogIGZpbHRlcihjb2RlID09ICJVU0EiKSAlPiUgDQogIGZpbHRlcihzZXggPT0gIm1hbGUiKSAlPiUgDQogIGZpbHRlcih5ZWFyID49IDE5NzApICU+JQ0KICBmaWx0ZXIoYWdlIDw9IDEwOSkgJT4lIA0KICBtdXRhdGUobG5NeCA9IGxvZyhNeCArIDAuMDAwMDEsIDEwKSkgJT4lIA0KICBzZWxlY3QoLWNvZGUsIC1zZXgsIC1NeCkgJT4lIA0KICBzcHJlYWQoeWVhciwgbG5NeCkgLT4gdG1wDQoNCm5tcyA8LSB0bXAkYWdlDQp0bXAkYWdlIDwtIE5VTEwNCnJvd25hbWVzKHRtcCkgPC0gbm1zDQp0bXANCg0KaGMgPC0gaGNsdXN0KGRpc3QodG1wKSwgImF2ZSIpDQoNCmdnZGVuZHJvOjpnZ2RlbmRyb2dyYW0oaGMsIHJvdGF0ZSA9IFRSVUUsIGxlYWZfbGFiZWxzID0gVFJVRSkgKyBjb29yZF9mbGlwKCkNCg0KDQoNCmBgYA0KDQoNClRoaXMgaGFzIGJlZW4gYSBiaXQgdG9vIG11Y2ggb2YgYSBkcmFpbiBvbiBteSBjb2duaXRpdmUgcmVzb3VyY2VzIGZvciBub3cuIEknbSBnb2luZyB0byBzdG9wIGFuZCByZWV2YWx1YXRlIHdoZW4gSSd2ZSBnb3QgbW9yZSBmb2N1cy4gSXQgZG9lcyBzZWVtIHRvIGlkZW50aWZ5IHR3byBicm9hZCBjbHVzdGVycywgYW5kIGZvciB0aGVzZSBjbHVzdGVycyB0byBiZSBhcyBleHBlY3RlZCwgc3BsaXQgYmV0d2VlbiB3b3JraW5nIGFnZSBhbmQgcmV0aXJlbW50IGFnZSwgd2l0aCBhZ2UgMCB3aXRoIHBvc3QgcmV0aXJlbWVudCBhZ2UuIA0KDQpMZXQncyBxdWlja2x5IHRyeSBQQ0Egb24gdGhlIHNhbWUNCg0KYGBge3J9DQpwYyA8LSBwcmNvbXAodG1wLCBzY2FsZSA9IFRSVUUsIGNlbnRlciA9IFRSVUUpDQoNCnBjX2RmIDwtIHRpYmJsZShhZ2UgPSByb3duYW1lcyhwYyR4KSwgcGMxID0gcGMkeFssMV0sIHBjMiA9IHBjJHhbLDJdLCBwYzMgPSBwYyR4WywzXSkNCg0KZ2dwbG90KHBjX2RmLCBhZXMoeCA9IHBjMSwgeSA9IHBjMiwgbGFiZWwgPSBhZ2UpKSArIA0KICBnZW9tX3RleHQoKQ0KDQpnZ3Bsb3QocGNfZGYsIGFlcyh4ID0gcGMyLCB5ID0gcGMzLCBsYWJlbCA9IGFnZSkpICsgDQogIGdlb21fdGV4dCgpDQoNCg0KYGBgDQoNCmBgYHtyfQ0KDQpkdGFfTXggJT4lIA0KICBmaWx0ZXIoY29kZSA9PSAiVVNBIikgJT4lIA0KICBmaWx0ZXIoc2V4ID09ICJmZW1hbGUiKSAlPiUgDQogIGZpbHRlcih5ZWFyID49IDE5NzApICU+JQ0KICBmaWx0ZXIoYWdlIDw9IDEwOSkgJT4lIA0KICBtdXRhdGUobG5NeCA9IGxvZyhNeCArIDAuMDAwMDEsIDEwKSkgJT4lIA0KICBzZWxlY3QoLWNvZGUsIC1zZXgsIC1NeCkgJT4lIA0KICBzcHJlYWQoeWVhciwgbG5NeCkgLT4gdG1wMg0KDQpubXMgPC0gdG1wMiRhZ2UNCnRtcDIkYWdlIDwtIE5VTEwNCnJvd25hbWVzKHRtcDIpIDwtIG5tcw0KdG1wMg0KDQpoYzIgPC0gaGNsdXN0KGRpc3QodG1wMiksICJhdmUiKQ0KDQpnZ2RlbmRybzo6Z2dkZW5kcm9ncmFtKGhjMiwgcm90YXRlID0gVFJVRSwgbGVhZl9sYWJlbHMgPSBUUlVFKSArIGNvb3JkX2ZsaXAoKQ0KDQoNCnBjMiA8LSBwcmNvbXAodG1wMiwgc2NhbGUgPSBUUlVFLCBjZW50ZXIgPSBUUlVFKQ0KDQpwYzJfZGYgPC0gcGMyJHhbLDE6M10gJT4lIGFzLnRpYmJsZSgpICU+JSBtdXRhdGUoYWdlID0gcm93bmFtZXMocGMyJHgpKQ0KDQpnZ3Bsb3QocGMyX2RmLCBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgbGFiZWwgPSBhZ2UpKSArIA0KICBnZW9tX3RleHQoKQ0KDQpnZ3Bsb3QocGMyX2RmLCBhZXMoeCA9IFBDMiwgeSA9IFBDMywgbGFiZWwgPSBhZ2UpKSArIA0KICBnZW9tX3RleHQoKQ0KDQoNCmBgYA0K